package com.health.user.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.health.api.client.MedicineClient;
import com.health.api.model.transporter.TransporterRegisterRequest;
import com.health.common.auth.model.enums.UserRoleEnum;
import com.health.common.auth.service.TokenService;
import com.health.common.auth.utils.SecurityUtils;
import com.health.common.common.ErrorCode;
import com.health.common.exception.BusinessException;
import com.health.common.model.user.LoginUser;
import com.health.common.model.vo.UserKeyVO;
import com.health.common.service.util.WeBASEUtils;
import com.health.user.mapper.PatientMapper;
import com.health.user.mapper.UserMapper;
import com.health.user.model.dto.doctor.DoctorRegisterRequest;
import com.health.user.model.dto.hospital.HospitalRegisterRequest;
import com.health.api.model.producer.ProducerRegisterRequest;
import com.health.user.model.dto.user.PatientRegisterRequest;
import com.health.user.model.entity.User;
import com.health.user.model.vo.LoginUserVO;
import com.health.user.model.vo.UserVO;
import com.health.user.service.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.fisco.bcos.sdk.v3.codec.ContractCodecException;
import org.fisco.bcos.sdk.v3.transaction.model.exception.TransactionBaseException;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.health.common.constant.UserConstant.*;


/**
 * 用户服务实现
 *
 * @author zhexueqi
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    /**
     * 盐值，混淆密码
     */
    public static final String SALT = "zhexueqi";

    @Resource
    private WeBASEUtils weBASEUtils;

    @Resource
    private PatientMapper patientMapper;

    @Resource
    private TokenService tokenService;

    @Resource
    private PatientService patientService;

    @Resource
    private HospitalService hospitalService;

    @Resource
    private DoctorService doctorService;

    @Resource
    private MedicineClient medicineClient;

    @Override
    @Transactional
    public long patientRegister(PatientRegisterRequest userRegisterRequest) {
        String userAccount = userRegisterRequest.getUserAccount();
        String name = userRegisterRequest.getName();
        String userPassword = userRegisterRequest.getUserPassword();
        long userId = insertUser(userAccount, name, userPassword, PATIENT_ROLE);
        //插入患者表
        patientService.createPatientToMysql(userRegisterRequest);
        return userId;
    }



    public void isRegistered(String userAccount) {
        //验证用户是否已经注册
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_account",userAccount);
        long count = this.count(queryWrapper);
        if (count > 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户已注册");
        }
    }


    /**
     * 用户注销
     *
     * @param request
     */
    @Override
    public boolean userLogout(HttpServletRequest request) {
        if (request.getHeader(AUTH_HEADER)== null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        String token = SecurityUtils.getToken(request);
        // 移除登录态
        tokenService.delLoginUser(token);
        return true;
    }

    @Override
    public LoginUserVO getLoginUserVO(HttpServletRequest request) {
        LoginUserVO loginUserVO = new LoginUserVO();
        LoginUser loginUser = tokenService.getLoginUser(request);
        BeanUtils.copyProperties(loginUser, loginUserVO);
        return loginUserVO;
    }

    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @Override
    public List<UserVO> getUserVO(List<User> userList) {
        if (CollUtil.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }

    @Override
    public UserKeyVO generateKey(HttpServletRequest request) throws TransactionBaseException, ContractCodecException {
        //获取当前登录用户
        LoginUser loginUser =  tokenService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        //判断是否已经生成公私钥对
        if (loginUser.getPublicKey() != null && !loginUser.getPublicKey().isEmpty()) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "已经生成公私钥对");
        }
        String userName = loginUser.getUserName();
        //生成公私钥对
        UserKeyVO userKeyVO = weBASEUtils.regBlockChainUser(userName);

        //根据loginUser的用户类型去执行不同角色的注册
        if (Objects.equals(loginUser.getUserRole(), PATIENT_ROLE)) {
            //注册区块链患者用户
            patientService.createPatient(userKeyVO,loginUser.getUserAccount());
        } else if (Objects.equals(loginUser.getUserRole(), HOSPITAL_ROLE)) {
            //注册区块链医院用户
            hospitalService.createHospital(userKeyVO,loginUser.getUserAccount());
        } else if (Objects.equals(loginUser.getUserRole(), DOCTOR_ROLE)){
            //注册区块链医生
            doctorService.createDoctor(userKeyVO,loginUser.getUserAccount());
        }else if (Objects.equals(loginUser.getUserRole(), PRODUCER_ROLE)){
            //注册区块链生产商
            boolean manufacturer = medicineClient.createManufacturer(userKeyVO, loginUser.getUserAccount());
            if (!manufacturer){
                throw new BusinessException(ErrorCode.OPERATION_ERROR,"生产商用户注册失败");
            }
        }else if (Objects.equals(loginUser.getUserRole(), TRANSPORTER_ROLE)){
            //注册区块链运输商
            medicineClient.createTransporter(userKeyVO,loginUser.getUserAccount());
        }
        else {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户类型错误");
        }
        //更新数据库信息，把公钥和地址存到数据库中
        loginUser.setPublicKey(userKeyVO.getPublicKey());
        loginUser.setUserAddress(userKeyVO.getAddress());
        User user = new User();
        BeanUtils.copyProperties(loginUser,user);
        this.updateById(user);
        tokenService.setLoginUser(loginUser);
        return userKeyVO;
    }

    @Override
    public LoginUser getUserInfoByUserAccountAndPassword(String userAccount, String userPassword, Integer userRole) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_account", userAccount);
        queryWrapper.eq("user_password", userPassword);
        User user = this.getOne(queryWrapper);
        if (user == null) {
            log.info("user login failed, userAccount cannot match userPassword");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        if (!Objects.equals(user.getUserRole(), userRole)){
            log.info("user login failed, userRole cannot match userRole");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户角色不匹配");
        }
        LoginUser loginUser = new LoginUser();
        BeanUtils.copyProperties(user, loginUser);
        return loginUser;
    }

    @Override
    public long hospitalRegister(HospitalRegisterRequest hospitalRegisterRequest) {
        if (hospitalRegisterRequest == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        String userAccount = hospitalRegisterRequest.getHospitalAccount();
        String name = hospitalRegisterRequest.getHospitalName();
        String userPassword = hospitalRegisterRequest.getHospitalPassword();

        long userId = insertUser(userAccount, name, userPassword, HOSPITAL_ROLE);

        //插入医院表
        hospitalService.createHospitalToMysql(hospitalRegisterRequest);
        return userId;
    }

    /**
     * 注册医生
     */
    @Override
    @Transactional
    public UserKeyVO doctorRegister(DoctorRegisterRequest doctorRegisterRequest) throws TransactionBaseException, ContractCodecException {
        if (doctorRegisterRequest == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        String userAccount = doctorRegisterRequest.getDoctorAccount();
        String userName = doctorRegisterRequest.getDoctorName();
        String userPassword = doctorRegisterRequest.getDoctorPassword();

        insertUser(userAccount, userName, userPassword, DOCTOR_ROLE);

        //生成公私钥对
        UserKeyVO userKeyVO = weBASEUtils.regBlockChainUser(userName);

        //插入医生表
        doctorService.createDoctorToMysql(doctorRegisterRequest,userKeyVO);

        //注册区块链医生用户
        doctorService.createDoctor(userKeyVO,userAccount);

        return userKeyVO;
    }

    @Override
    public List<String> getRoleList(Long loginId) {
        User user = this.getById(loginId);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        List<String> roleList = new ArrayList<>();
        if (Objects.equals(user.getUserRole(), PATIENT_ROLE)) {
            roleList.add(UserRoleEnum.PATIENT.getRole());
        } else if (Objects.equals(user.getUserRole(), DOCTOR_ROLE)) {
            roleList.add(UserRoleEnum.DOCTOR.getRole());
        } else if (Objects.equals(user.getUserRole(), HOSPITAL_ROLE)) {
            roleList.add(UserRoleEnum.HOSPITAL.getRole());
        } else if (Objects.equals(user.getUserRole(), PRODUCER_ROLE)) {
            roleList.add(UserRoleEnum.PRODUCER.getRole());
        } else if (Objects.equals(user.getUserRole(), TRANSPORTER_ROLE)) {
            roleList.add(UserRoleEnum.TRANSPORTER.getRole());
        }
        return roleList;
    }

    @Override
    @Transactional
    public long producerRegister(ProducerRegisterRequest producerRegisterRequest) {
        String userAccount = producerRegisterRequest.getManufacturerAccount();
        String manufacturerName = producerRegisterRequest.getName();
        String userPassword = producerRegisterRequest.getUserPassword();

        long userId = insertUser(userAccount, manufacturerName, userPassword, PRODUCER_ROLE);
        Boolean manufacturerToMysql = medicineClient.createManufacturerToMysql(producerRegisterRequest);
        if (Boolean.FALSE.equals(manufacturerToMysql)){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"生产商用户注册失败");
        }
        return userId;
    }

    @Override
    @Transactional
    public Long transportRegister(TransporterRegisterRequest transportRegisterRequest) {
        String userAccount = transportRegisterRequest.getTransportAccount();
        String manufacturerName = transportRegisterRequest.getName();
        String userPassword = transportRegisterRequest.getPassword();

        long userId = insertUser(userAccount, manufacturerName, userPassword, TRANSPORTER_ROLE);
        Boolean transporterToMysql = medicineClient.createTransporterToMysql(transportRegisterRequest);
        if (Boolean.FALSE.equals(transporterToMysql)){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"运输商用户注册失败");
        }
        return userId;
    }

    /**
     * 插入用户到数据库
     */
    private long insertUser(String userAccount, String name, String userPassword,Integer userRole) {
        //校验是否已注册
        isRegistered(userAccount);
        //密码加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        //设置用户信息
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserName(name);
        user.setUserPassword(encryptPassword);
        user.setUserRole(userRole); //为1 患者
        //插入数据
        boolean res = this.save(user);
        if (!res){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"注册失败");
        }
        return user.getId();
    }

}
